﻿namespace CalculatorLibrary
{
    using System.Text;
    using System.Text.RegularExpressions;

    using CalculatorLibrary.ExpressionLibrary;

    /// <summary>
    /// Class Validator
    /// </summary>
    public class Validator
    {
        #region Constants

        /// <summary>
        ///     The left number group
        /// </summary>
        private const string LeftNumberGroup = "LeftNumber";

        /// <summary>
        ///     The operator group
        /// </summary>
        private const string OperatorGroup = "Operator";

        /// <summary>
        ///     The right number group
        /// </summary>
        private const string RightNumberGroup = "RightNumber";

        #endregion

        #region Fields

        private readonly CalculationResult _errorResult;

        private readonly string _regularExpression;

        private UserExpression _expression;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="Validator" /> class.
        /// </summary>
        /// <param name="decimalDelimiter">The decimal delimiter.</param>
        public Validator(string decimalDelimiter)
        {
            _errorResult = new CalculationResult { IsError = true, ReturnState = ReturnState.Continue };

            _regularExpression = GenerateRegularExpression(decimalDelimiter);
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// Gets the error.
        /// </summary>
        /// <returns>CalculationResult.</returns>
        public CalculationResult GetError()
        {
            return _errorResult;
        }

        /// <summary>
        /// Gets the user expression.
        /// </summary>
        /// <returns>UserExpression.</returns>
        public UserExpression GetUserExpression()
        {
            return _expression;
        }

        /// <summary>
        /// Validates the specified input.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
        public bool Validate(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                _errorResult.StringResult = "Can't process empty input.";
                return false;
            }

            var regex = new Regex(_regularExpression);
            if (!regex.IsMatch(input))
            {
                _errorResult.StringResult = string.Format("Expression is not valid.");
                return false;
            }

            var groupCollection = regex.Match(input).Groups;
            var operatorString = groupCollection[OperatorGroup].Value;
            if (!TypesCollection.CalculationTypes.ContainsKey(operatorString))
            {
                _errorResult.StringResult = "Operator is not allowed.";
                return false;
            }

            var leftNumber = ParseOperand(groupCollection[LeftNumberGroup].Value);
            var rightNumber = ParseOperand(groupCollection[RightNumberGroup].Value);

            _expression = new UserExpression
                              {
                                  CalculationType = TypesCollection.CalculationTypes[operatorString], 
                                  LeftOperand = leftNumber, 
                                  RightOperand = rightNumber
                              };

            return true;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Generates the regular expression.
        /// </summary>
        /// <param name="decimalDelimiter">The decimal delimiter.</param>
        /// <returns>System.String.</returns>
        private string GenerateRegularExpression(string decimalDelimiter)
        {
            var sb = new StringBuilder();
            var leftPart = string.Format(@"(?<{0}>(\+|\-)?\d+((\{1})\d+)?)", LeftNumberGroup, decimalDelimiter);
            var operatorString = string.Format(@"(?<{0}>.*)", OperatorGroup);
            var rightPart = string.Format(@"(?<{0}>(\+|\-)?\d+((\{1})\d+)?)", RightNumberGroup, decimalDelimiter);
            sb.Append("^");
            sb.Append(leftPart);
            sb.Append(operatorString);
            sb.Append(rightPart);
            sb.Append("$");
            return sb.ToString();
        }

        /// <summary>
        ///     Determines whether [is digits only] [the specified STR].
        /// </summary>
        /// <param name="str">
        ///     The STR.
        /// </param>
        /// <returns>
        ///     <c>true</c> if [is digits only] [the specified STR]; otherwise, <c>false</c>.
        /// </returns>
        private bool IsDigitsOnly(string str)
        {
            foreach (char c in str)
            {
                if (c < '0' || c > '9')
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        ///     Parses the operand.
        /// </summary>
        /// <param name="input">
        ///     The input.
        /// </param>
        /// <returns>
        ///     System.Decimal.
        /// </returns>
        private decimal ParseOperand(string input)
        {
            if (IsDigitsOnly(input))
            {
                return int.Parse(input);
            }

            decimal value;
            decimal.TryParse(input, out value);
            return value;
        }

        #endregion
    }
}